package com.musicdaze.utils.math;

/**
 * Represents a vector with 3 values
 * @author mfujihara
 * 
 */
public class Vector3 {
  /** x value **/
  public float x;
  /** y value **/
  public float y;
  /** z value **/
  public float z;

  /**
   * public constructor that sets x=y=z=0
   */
  public Vector3() {
    this.x = 0;
    this.y = 0;
    this.z = 0;
  }
  /**
   * public constructor thats uses passed in vector
   * for x,y, and z
   * @param v the vector to use for instantiation
   */
  public Vector3(Vector3 v){
    this.x = v.x;
    this.y = v.y;
    this.z = v.z;
  }
  /**
   * public contructor that sets x y and z
   * @param x value to set x as
   * @param y value to set y as
   * @param z value to set z as
   */
  public Vector3(float x, float y, float z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }

  /**
   * adds v to this vector. mutate
   * @param v the vector to add to this
   * @return this
   */
  public Vector3 add(Vector3 v) {
    this.x += v.x;
    this.y += v.y;
    this.z += v.z;
    return this;
  }
  /**
   * subtracts v from this vector. mutate
   * @param v the vector to subtract from this
   * @return this
   */
  public Vector3 sub(Vector3 v){
    this.x -= v.x;
    this.y -= v.y;
    this.z -= v.z;
    return this;
  }
  /**
   * sets this with values from another vector. mutate
   * @param as the vector to set values from
   */
  public void set(Vector3 as) {
    this.x = as.x;
    this.y = as.y;
    this.z = as.z;
  }

  /**
   * scales this vector by a value. mutate
   * @param s the value to scale this as
   * @return this
   */
  public Vector3 scale(float s) {
    this.x *= s;
    this.y *= s;
    this.z *= s;
    return this;
  }
  /**
   * dot product
   * @param v the vector to dot with
   * @return the dot product
   */
  public float dot(Vector3 v){
    return x*v.x + y*v.y + z*v.z;
  }
  /**
   * crosses this with input vector 
   * @param v the vector to cross this with
   * @return the cross of this and v
   */
  public Vector3 cross(Vector3 v){
    Vector3 cross = new Vector3();
    cross.x = y*v.z - v.y*z;
    cross.y = -(x*v.z - v.x*z);
    cross.z = x*v.y - v.x*y;
    return cross;
  }
  /**
   * Returns the magnitude of this vector
   * @return the magnitude of this vector
   */
  public float mag() {
    return (float) Math.sqrt(x * x + y * y + z * z);
  }

  /**
   * Normalizes this vector.
   * @return this
   */
  public Vector3 normalize() {
    this.scale(1 / mag());
    return this;
  }

  /**
   * gets the distance from this vector to point assuming both are points in 3d
   * space
   * @param point the point to find the distance to
   * @return the distance between this and point
   */
  public float distanceTo(Vector3 point) {
    Vector3 diff = Vector3.sub(this, point);
    return diff.mag();
  }

  /**
   * compares x y and z
   * @param o the object to check equality with
   * @return true if this equals the object based on x, y, z; false otherwise
   */
  public boolean equals(Object o) {
    if (o instanceof Vector3) {
      Vector3 v = (Vector3) o;
      return v.x == this.x && v.y == this.y && this.z == v.z;
    }
    return false;
  }

  /**
   * uses (int)(x + y + z)
   * @return hashcode
   */
  public int hashCode() {
    return (int) (x + y + z);
  }

  /**
   * prints out (x,y,z)
   * @return (x,y,z)
   */
  @Override
  public String toString() {
    return "(" + this.x + "," + this.y + "," + this.z + ")";
  }

  /**
   * adds two vectors together without mutating them.
   * @param a a vector to add
   * @param b a vector to add
   * @return a vector that represents the sum of a and b
   */
  public static Vector3 add(Vector3 a, Vector3 b) {
    Vector3 ret = new Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
    return ret;
  }

  /**
   * a cross b
   * @param a the vector to cross with
   * @param b the vector to cross with
   * @return a cross b
   */
  public static Vector3 cross(Vector3 a, Vector3 b){
    Vector3 cross = new Vector3();
    cross.x = a.y*b.z - b.y*a.z;
    cross.y = -(a.x*b.z - b.x*a.z);
    cross.z = a.x*b.y - b.x*a.y;
    return cross;
  }
  /**
   * subtracts two vectors without mutation.
   * @param a a vector to subtract from
   * @param b a vector to subtract
   * @return Vector2 that is a-b
   */
  public static Vector3 sub(Vector3 a, Vector3 b) {
    Vector3 ret = new Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
    return ret;
  }

  /**
   * Gets the angle between a to b
   * @param a the start vector
   * @param b the end vector
   * @return the angle between two vectors
   */
  public static float angleBetween(Vector3 a, Vector3 b){
    float dot = a.dot(b);
    dot = dot/a.mag();
    dot = dot/b.mag();
    return (float) Math.acos(dot);
  }
}
